package com.iot.server.common.core;

import com.iot.core.constant.ResponseEnum;
import com.iot.core.domain.BaseDO;
import com.iot.core.domain.PageDO;
import com.iot.core.exception.BDException;
import com.iot.core.mybatis.interceptor.ExecutorInterceptor;
import com.iot.core.utils.ListUtils;
import com.iot.server.enumeration.TimeEnum;
import com.iot.server.service.DictService;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public abstract class BaseMapperImpl<T extends BaseDO> extends SqlSessionDaoSupport implements BaseMapper<T> {

    protected static final Logger log = LoggerFactory.getLogger(BaseMapperImpl.class);

    /**
     * 注入SqlSessionTemplate实例(要求Spring中进行SqlSessionTemplate的配置).<br/>
     * 可以调用sessionTemplate完成数据库操作.
     */
    @Autowired
    private SqlSessionTemplate sessionTemplate;
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    @Autowired
    @Lazy
    private DictService dictService;

    protected SqlSessionTemplate getSessionTemplate() {
        return sessionTemplate;
    }

    @Autowired
    public void setSessionTemplate(SqlSessionTemplate sessionTemplate) {
        super.setSqlSessionTemplate(sessionTemplate);
    }

    @Autowired
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        super.setSqlSessionFactory(sqlSessionFactory);
    }


    public SqlSession getSqlSession() {
        return super.getSqlSession();
    }

    public int insert(T t) {
        if (t == null) {
            throw new RuntimeException("T is null");
        }
        //将值改回数据字典
//        dictService.setNameToDict(t);
        //设置创建时间和更新时间
        this.setCreateDtAndUpdateDt(t, TimeEnum.CREATETIME_UPDATETIME);
        int result = sessionTemplate.insert(getStatement(SQL_INSERT), t);
        if (result <= 0) {
            throw new BDException(ResponseEnum.ERROR.getMsg(), ResponseEnum.ERROR.getCode());
        }
        return result;
    }

    private void setCreateDtAndUpdateDt(T t, TimeEnum timeEnum) {
        Class clazz = t.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        Method createDtMethod = null;
        Method updateDtMethod = null;
        for (int i = 0; i < methods.length; i++) {
            if (methods[i].getName().equals("setCreateDt")) {
                createDtMethod = methods[i];
                createDtMethod.setAccessible(true);
            }
            if (methods[i].getName().equals("setUpdateDt")) {
                updateDtMethod = methods[i];
                updateDtMethod.setAccessible(true);
            }
        }
        Date date = new Date();
        switch (timeEnum) {
            case CREATETIME:
                //创建时间
                if (null != createDtMethod) {
                    setTime(t, createDtMethod, date);
                }
                break;
            case UPDATETIME:
                //更新时间
                if (null != updateDtMethod) {
                    setTime(t, updateDtMethod, date);
                }
                break;
            case CREATETIME_UPDATETIME:
                //创建时间and更新时间
                if (null != createDtMethod) {
                    setTime(t, createDtMethod, date);
                }
                if (null != updateDtMethod) {
                    setTime(t, updateDtMethod, date);
                }
                break;
            default:
                break;
        }
    }

    public void setTime(T t, Method method, Object data) {
        try {
            method.invoke(t, data);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public int insertSelective(T t) {
        if (t == null) {
            throw new RuntimeException("T is null");
        }
        //将值改回数据字典
//        dictService.setNameToDict(t);
        int result = sessionTemplate.insert(getStatement(SQL_INSERT_SELECTIVE), t);
        if (result <= 0) {
            throw new BDException(ResponseEnum.ERROR.getMsg(), ResponseEnum.ERROR.getCode());
        }
        return result;
    }

    public int batchInsert(List<T> list) {
        if (list == null || list.size() <= 0) {
            return 0;
        }
        int result = sessionTemplate.insert(getStatement(SQL_BATCH_INSERT), list);
        if (result <= 0) {
            throw new BDException(ResponseEnum.ERROR.getMsg(), ResponseEnum.ERROR.getCode());
        }
        return result;
    }

    public int batchInsert(List<T> list, int size) {
        int result = 0;
        List<List<T>> listList = ListUtils.segmentationList(list, size);
        for (List<T> data : listList) {
            result += batchInsert(data);
        }
        return result;
    }

    public int insertByParams(String sqlId, Object parameter) {
        return sessionTemplate.insert(getStatement(sqlId), parameter);
    }

    public int deleteByPrimaryKey(String id) {
        return sessionTemplate.delete(getStatement(SQL_DELETE_BY_ID), id);
    }

    public int deleteByEntity(T record) {
        return sessionTemplate.delete(getStatement(SQL_DELETE_BY_ENTITY), record);
    }

    public int deleteByParams(String sqlId, Object parameter) {
        return sessionTemplate.delete(getStatement(sqlId), parameter);
    }

    public int batchDeleteByPrimaryKey(List<String> ids) {
        if (ids == null || ids.size() == 0) {
            return 0;
        }
        return sessionTemplate.delete(getStatement(SQL_BATCH_DELETE_BY_IDS), ids);
    }

    public int batchDeleteByMap(Map<String, Object> params) {
        return sessionTemplate.delete(getStatement(SQL_BATCH_DELETE_BY_MAP), params);
    }

    public T selectByPrimaryKey(String id) {
        T t = sessionTemplate.selectOne(getStatement(SQL_SELECT_BY_ID), id);
        if (null != t) {
//            dictService.setDictName(t, t.getClass());
        }
        return t;
    }

    public T selectByMap(Map<String, Object> paramMap) {
        return this.selectByParams(SQL_SELECT_BY_MAP, paramMap);
    }

    public T selectByEntity(T entity) {
        return sessionTemplate.selectOne(getStatement(SQL_SELECT_BY_ENTITY), entity);
    }

    public T selectByEntity(String sqlId, T entity) {
        return sessionTemplate.selectOne(sqlId, entity);
    }

    public T selectByParams(String sqlId, Map<String, Object> paramMap) {
        if (paramMap == null || paramMap.isEmpty())
            return null;
        return this.getSqlSession().selectOne(getStatement(sqlId), paramMap);
    }

    public int updateByPrimaryKeySelective(T t) {
        if (t == null) {
            throw new RuntimeException("T is null");
        }
        //将值改回数据字典
//        dictService.setNameToDict(t);
        this.setCreateDtAndUpdateDt(t, TimeEnum.UPDATETIME);
        int result = sessionTemplate.update(getStatement(SQL_UPDATE_SELECTIVE), t);
        if (result <= 0) {
            throw new BDException(ResponseEnum.ERROR.getMsg(), ResponseEnum.ERROR.getCode());
        }
        return result;
    }

    public int updateByPrimaryKey(T t) {
        if (t == null) {
            throw new RuntimeException("T is null");
        }
        //将值改回数据字典
//        dictService.setNameToDict(t);
        this.setCreateDtAndUpdateDt(t, TimeEnum.UPDATETIME);
        int result = sessionTemplate.update(getStatement(SQL_UPDATE), t);
        if (result <= 0) {
            throw new BDException(ResponseEnum.ERROR.getMsg(), ResponseEnum.ERROR.getCode());
        }
        return result;
    }

    public int updateByParams(String sqlId, Object parameter) {
        return sessionTemplate.update(getStatement(sqlId), parameter);
    }

    @Override
    public int getCountByEntity(String sqlId, T entity) {
        Object countObject = getSqlSession().selectOne(getStatement(sqlId), new ExecutorInterceptor.CountParameter(entity));
        return Integer.parseInt(countObject.toString());
    }

    @Override
    public Object getlistResultByEntity(String sqlId, T entity) {
        return sessionTemplate.selectList(getStatement(sqlId), entity);
    }

    @Override
    public List<T> selectListPageByEntity(String sqlId, T entity) {
        return getSqlSession().selectList(getStatement(sqlId), entity);
    }

    public PageDO<T> listByPage(T entity, String sqlId) {
        PageDO<T> resPage = new PageDO<>();
        List<T> list;
        int pageNo = entity.getPageSO().getPage() == null ? 1 : entity.getPageSO().getPage();
        int pageSize = entity.getPageSO().getLimit() == null ? 10 : entity.getPageSO().getLimit();
        int count = 0;
        int offset;
        Object countObject = getSqlSession().selectOne(getStatement(sqlId), new ExecutorInterceptor.CountParameter(entity));
        count = Integer.valueOf(countObject.toString());
        resPage.setTotal(count);
        if (pageSize == -1) {
            list = getSqlSession().selectList(getStatement(sqlId), entity);
        } else {
            if (count < pageNo * pageSize) {
                if (count % pageSize == 0) {
                    offset = ((count / pageSize) - 1) * pageSize;
                } else {
                    offset = (count / pageSize) * pageSize;
                }
            } else {
                offset = (pageNo - 1) * pageSize;
            }
            list = getSqlSession().selectList(getStatement(sqlId), entity, new RowBounds(offset, pageSize));
        }
        dictService.setDictName(list, entity.getClass());
        resPage.setRows(list);
        return resPage;
    }

    public PageDO<T> listByPage(T entity) {
        return this.listByPage(entity, SQL_LIST_BY_PAGE);
    }

    @Override
    public <E extends BaseDO> PageDO<E> listByPage(T entity, Class<E> clazz, String sqlId) {
        PageDO<E> resPage = new PageDO<>();
        List<E> list;
        int pageNo = entity.getPageSO().getPage() == null ? 1 : entity.getPageSO().getPage();
        int pageSize = entity.getPageSO().getLimit() == null ? 10 : entity.getPageSO().getLimit();
        int count = 0;
        int offset;
        Object countObject = getSqlSession().selectOne(getStatement(sqlId), new ExecutorInterceptor.CountParameter(entity));
        count = Integer.valueOf(countObject.toString());
        resPage.setTotal(count);
        if (pageSize == -1) {
            list = getSqlSession().selectList(getStatement(sqlId), entity);
        } else {
            if (count < pageNo * pageSize) {
                if (count % pageSize == 0) {
                    offset = ((count / pageSize) - 1) * pageSize;
                } else {
                    offset = (count / pageSize) * pageSize;
                }
            } else {
                offset = (pageNo - 1) * pageSize;
            }
            list = getSqlSession().selectList(getStatement(sqlId), entity, new RowBounds(offset, pageSize));
        }
//        dictService.setDictName(list, clazz);
        resPage.setRows(list);
        return resPage;
    }

    @Override
    public <E extends BaseDO> PageDO<E> listOtherPage(E entity, Class<E> clazz, String sqlId) {
        PageDO<E> resPage = new PageDO<>();
        List<E> list;
        int pageNo = entity.getPageSO().getPage() == null ? 1 : entity.getPageSO().getPage();
        int pageSize = entity.getPageSO().getLimit() == null ? 10 : entity.getPageSO().getLimit();
        int count = 0;
        int offset;
        Object countObject = getSqlSession().selectOne(getStatement(sqlId), new ExecutorInterceptor.CountParameter(entity));
        count = Integer.valueOf(countObject.toString());
        resPage.setTotal(count);
        if (pageSize == -1) {
            list = getSqlSession().selectList(getStatement(sqlId), entity);
        } else {
            if (count < pageNo * pageSize) {
                if (count % pageSize == 0) {
                    offset = ((count / pageSize) - 1) * pageSize;
                } else {
                    offset = (count / pageSize) * pageSize;
                }
            } else {
                offset = (pageNo - 1) * pageSize;
            }
            list = getSqlSession().selectList(getStatement(sqlId), entity, new RowBounds(offset, pageSize));
        }
//        dictService.setDictName(list, clazz);
        resPage.setRows(list);
        return resPage;
    }

    public List<T> listByMap(Map<String, Object> paramMap) {
        return (List) this.listByMap(paramMap, SQL_LIST_BY_MAP);
    }

    public List<T> listByMap(Map<String, Object> paramMap, String sqlId) {
        if (paramMap == null) {
            paramMap = new HashMap<>();
        }
        return sessionTemplate.selectList(getStatement(sqlId), paramMap);
    }

    @Override
    public List<T> listByEntity(T entity) {
        return this.listByEntity(entity, SQL_LIST_BY_ENTITY);
    }

    @Override
    public List<T> listByEntity(T entity, String sqlId) {
        return sessionTemplate.selectList(getStatement(sqlId), entity);
    }

    protected String getStatement(String sqlId) {
        String name = this.getClass().getName();
        return name + "." + sqlId;
    }

    public Object selectByMap(String sqlId, Map<String, Object> paramMap) {
        return sessionTemplate.selectList(getStatement(sqlId), paramMap);
    }

    public int getCountByParams(String sqlId, Map<String, Object> paramMap) {
        if (paramMap == null || paramMap.isEmpty())
            return 0;
        Object countObject = getSqlSession().selectOne(getStatement(sqlId), paramMap);
        return Integer.parseInt(countObject.toString());
    }

    @Override
    public int updateBatch(List<T> list) {
        list.forEach(t -> {
            updateByPrimaryKey(t);
        });
        return 0;
    }
}
